<?php
/**
 * This file houses the Inflector class.
 *
 * Copyright (c) 2010, Kalkomey Enterprises, Inc.
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * 
 * - Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright notice, this 
 *   list of conditions and the following disclaimer in the documentation and/or other
 *   materials provided with the distribution.
 * - Neither the name of the Kalkomey Enterprises, Inc. nor the names of its 
 *   contributors may be used to endorse or promote products derived from this 
 *   software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
 * @copyright Copyright (c) 2010, Kalkomey Enterprises, Inc.
 * @link http://code.google.com/p/swerv
 * @package Framework
 * @subpackage Library
 */

/**
 * The Inflector class is used to convert words between singular and plural versions.
 *
 * @package Framework
 * @subpackage Library
 */
class Inflector
{

	/**
	 * A list of patterns to convert singular to plural.
	 *
	 * @var array
	 */
	static $plural = array (
		'/(quiz)$/i' => "$1zes",
		'/^(ox)$/i' => "$1en",
		'/^(o)$/i' => "$1es",
		'/([m|l])ouse$/i' => "$1ice",
		'/(matr|vert|ind)ix|ex$/i' => "$1ices",
		'/(x|ch|ss|sh)$/i' => "$1es",
		'/([^aeiouy]|qu)y$/i' => "$1ies",
		'/(hive)$/i' => "$1s",
		'/(?:([^f])fe|([lr])f)$/i' => "$1$2ves",
		'/(shea|lea|loa|thie|roo|hoo)f$/i' => "$1ves",
		'/sis$/i' => "ses",
		'/([ti])um$/i' => "$1a",
		'/(tomat|potat|buffal|ech|her|vet)o$/i' => "$1oes",
		'/(bu)s$/i' => "$1ses",
		'/(alias)$/i' => "$1es",
		'/(octop)us$/i' => "$1i",
		'/(ax|test)is$/i' => "$1es",
		'/(us)$/i' => "$1es",
		'/s$/i' => "s",
		'/$/' => "s",
		'/^$/' => '',
		'/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|vir|octop)us$/i' => "$1i",
		'/us$/i' => "uses",
	);

	/**
	 * A list of patterns to convert plural to singular.
	 *
	 * @var array
	 */
	static $singular = array (
		'/(shoe)s$/i' => "$1",
		'/oes$/i' => "$1o",
		'/(quiz)zes$/i' => "$1",
		'/(matr)ices$/i' => "$1ix",
		'/(vert|ind)ices$/i' => "$1ex",
		'/^(ox)en$/i' => "$1",
		'/(alias)es$/i' => "$1",
		'/(octop|vir)i$/i' => "$1us",
		'/(cris|ax|test)es$/i'=> "$1is",
		'/(o)es$/i' => "$1",
		'/(bus)es$/i' => "$1",
		'/([m|l])ice$/i' => "$1ouse",
		'/([d])ice$/i' => "$1ie",
		'/(x|ch|ss|sh)es$/i' => "$1",
		'/(m)ovies$/i' => "$1ovie",
		'/(s)eries$/i' => "$1eries",
		'/([^aeiouy]|qu)ies$/i' => "$1y",
		'/([lr])ves$/i' => "$1f",
		'/(tive)s$/i' => "$1",
		'/(hive)s$/i' => "$1",
		'/(li|wi|kni)ves$/i' => "$1fe",
		'/(shea|loa|lea|thie)ves$/i' => "$1f",
		'/(^analy)ses$/i' => "$1sis",
		'/(ax|test)es$/i' => "$1is",
		'/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => "$1$2sis",
		'/([ti])a$/i' => "$1um",
		'/(n)ews$/i' => "$1ews",
		'/(h|bl)ouses$/i'=> "$1ouse",
		'/(corpse)s$/i' => "$1",
		'/(us)es$/i' => "$1",
		'/s$/i' => ""
	);

	/**
	 * A list of patterns of words that have irregular plural forms
	 *
	 * @var array
	 */
	static $irregular = array (
		'dies' => 'die',
		'dice' => 'die',
		'hooves' => 'hoof',
		'rooves' => 'roof',
		'octopi' => 'octopus',
		'statuses' => 'status',
		'men' => 'man',
		'women' => 'woman',
		'children' => 'child',
		'people' => 'person',
		'atlases' => 'atlas',
		'beefs' => 'beef',
		'brothers' => 'brother',
		'corpuses' => 'corpus',
		'cows' => 'cow',
		'feet' => 'foot',	   
		'ganglions' => 'ganglion',
		'geese' => 'gooze',
		'genies' => 'genie',
		'genera' => 'genus',
		'monies' => 'money',
		'mongooses' => 'mongoose',
		'moves' => 'move',
		'mythoi' => 'mythos',
		'numina' => 'numen',
		'occiputs' => 'occiput',
		'opuses' => 'opus',
		'penises' => 'penis',
		'people' => 'person',
		'sexes' => 'sex',
		'soliloquies' => 'soliloquy',
		'teeth' => 'tooth',
		'trilbys' => 'trilby',
		'turfs' => 'turf'		
	);

	/**
	 * A list of patterns of words that are the same whether they are plural or singular.
	 *
	 * @var array
	 */
	static $uncountable = array (
		'.*[nrlm]ese',
		'.*deer',
		'.*fish',
		'.*measles',
		'.*ois',
		'.*pox',
		'.*sheep',
		'bass',
		'Amoyese',
		'bison', 
		'Borghese', 
		'bream', 
		'breeches', 
		'britches', 
		'buffalo', 
		'cantus', 
		'carp', 
		'chassis', 
		'clippers',
		'cod', 
		'coitus', 
		'Congoese', 
		'contretemps', 
		'corps', 
		'debris', 
		'diabetes', 
		'djinn', 
		'eland', 
		'elk',
		'equipment', 
		'Faroese', 
		'flounder', 
		'Foochowese', 
		'gallows', 
		'Genevese', 
		'Genoese', 
		'Gilbertese', 
		'headquarters', 
		'herpes', 
		'hijinks', 
		'Hottentotese', 
		'information', 
		'graffiti',
		'innings', 
		'jackanapes', 
		'Kiplingese',
		'Kongoese', 
		'Lucchese', 
		'mackerel', 
		'Maltese', 
		'media', 
		'mews', 
		'money',
		'moose', 
		'mumps', 
		'Nankingese', 
		'news',
		'nexus', 
		'Niasese', 
		'Pekingese', 
		'Piedmontese', 
		'pincers', 
		'Pistoiese', 
		'pliers', 
		'Portuguese', 
		'proceedings',
		'rabies', 
		'rice', 
		'rhinoceros', 
		'salmon', 
		'Sarawakese', 
		'scissors', 
		'sea[- ]bass', 
		'series', 
		'Shavese', 
		'shears',
		'siemens', 
		'species', 
		'swine', 
		'trousers', 
		'trout', 
		'tuna', 
		'Vermontese', 
		'Wenchowese',
		'whiting', 
		'wildebeest', 
		'Yengeese'		
	);

	/**
	 * Converts string from plural to singular.
	 *
	 * @param string $string the string to convert
	 *
	 * @return string
	 */
	public static function pluralize($string) {
		// save some time in the case that singular and plural are the same
		foreach (self::$uncountable as $pattern) {
			$pattern = '/' . $pattern . '$/i';
			if (preg_match($pattern, $string)) {
				return $string;
			}
		}

		// check for irregular singular forms
		foreach (self::$irregular as $result => $pattern) {
			$pattern = '/' . $pattern . '$/i';
			if (preg_match( $pattern, $string)) {
				return preg_replace($pattern, $result, $string);
			}
		}

		// check for matches using regular expressions
		foreach (self::$plural as $pattern => $result) {
			if (preg_match($pattern, $string)) {
				return preg_replace($pattern, $result, $string);
			}
		}
		
		// no matches just return
		return $string;
	}

	/**
	 * Converts string from singular to plural.
	 *
	 * @param string $string the string to convert
	 *
	 * @return string
	 */
	public static function singularize($string) {
		
		// save some time in the case that singular and plural are the same
		foreach (self::$uncountable as $pattern) {
			$pattern = '/' . $pattern . '$/i';
			if (preg_match($pattern, $string)) {
				return $string;
			}
		}

		// check for irregular plural forms
		foreach (self::$irregular as $pattern => $result) {
			$pattern = '/' . $pattern . '$/i';
			if (preg_match($pattern, $string)) {
				return preg_replace( $pattern, $result, $string);
			}
		}

		// check for matches using regular expressions
		foreach (self::$singular as $pattern => $result) {
			if (preg_match($pattern, $string)) {
				return preg_replace($pattern, $result, $string);
			}
		}

		// no matches, just return
		return $string;
	}
}
